package com.anyext.storage.impl;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;

import com.anyext.storage.JdbcTemplate;
import com.anyext.storage.SessionException;
import com.anyext.storage.StorageContext;
import com.anyext.storage.group.GroupDescriptor;
import com.anyext.storage.group.Group;
import com.anyext.storage.jdbc.ConnectionCallback;
import com.anyext.storage.jdbc.ResultSetExtractor;
import com.anyext.storage.jdbc.RowCallbackHandler;
import com.anyext.storage.jdbc.RowMapper;

public class JdbcTemplateImpl implements JdbcTemplate {
	//thread safety, instance once
	private org.springframework.jdbc.core.JdbcTemplate	jdbcTemplate;

	public JdbcTemplateImpl() {
		this.jdbcTemplate = new org.springframework.jdbc.core.JdbcTemplate();
	}

	private DataSource getDataSource() {
		Group dataSourceGroup = StorageContext.getRoutingGroup();
		GroupDescriptor dataSourceDescriptor = dataSourceGroup.getRoutingGroupDescriptor();
		return dataSourceDescriptor.getDataSource();
	}

	@Override
	public <T> T execute(final ConnectionCallback<T> action) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.execute(new org.springframework.jdbc.core.ConnectionCallback<T>() {

				@Override
				public T doInConnection(Connection con) throws SQLException, DataAccessException {
					return action.doInConnection(con);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("ConnectionCallback: " + action.getSql(), e);
		}
		return t;
	}

	@Override
	public void execute(String sql) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		try {
			jdbcTemplate.execute(sql);
		} catch (DataAccessException e) {
			throw new SessionException("execute: " + sql, e);
		}
	}

	@Override
	public <T> T query(String sql, final ResultSetExtractor<T> rse) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.query(sql, new org.springframework.jdbc.core.ResultSetExtractor<T>() {

				@Override
				public T extractData(ResultSet rs) throws SQLException, DataAccessException {
					return rse.extractData(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return t;
	}

	@Override
	public void query(String sql, final RowCallbackHandler rch) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		try {
			jdbcTemplate.query(sql, new org.springframework.jdbc.core.RowCallbackHandler() {

				@Override
				public void processRow(ResultSet rs) throws SQLException {
					rch.processRow(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
	}

	@Override
	public <T> List<T> query(String sql, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.query(sql, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> T queryForObject(String sql, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public Map<String, Object> queryForMap(String sql) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		Map<String, Object> map = null;
		try {
			map = jdbcTemplate.queryForMap(sql);
		} catch (DataAccessException e) {
			throw new SessionException("queryForMap: " + sql, e);
		}
		return map;
	}

	@Override
	public List<Map<String, Object>> queryForList(String sql) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<Map<String, Object>> list = null;
		try {
			list = jdbcTemplate.queryForList(sql);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> List<T> queryForList(String sql, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public int[] batchUpdate(String[] sql) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		int[] is = new int[0];
		try {
			is = jdbcTemplate.batchUpdate(sql);
		} catch (DataAccessException e) {
			throw new SessionException("batchUpdate: " + Arrays.asList(sql), e);
		}
		return is;
	}

	@Override
	public <T> T query(String sql, Object[] args, int[] argTypes, final ResultSetExtractor<T> rse) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.query(sql, args, argTypes, new org.springframework.jdbc.core.ResultSetExtractor<T>() {

				@Override
				public T extractData(ResultSet rs) throws SQLException, DataAccessException {
					return rse.extractData(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T query(String sql, Object[] args, final ResultSetExtractor<T> rse) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.query(sql, args, new org.springframework.jdbc.core.ResultSetExtractor<T>() {

				@Override
				public T extractData(ResultSet rs) throws SQLException, DataAccessException {
					return rse.extractData(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T query(String sql, final ResultSetExtractor<T> rse, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.query(sql, new org.springframework.jdbc.core.ResultSetExtractor<T>() {

				@Override
				public T extractData(ResultSet rs) throws SQLException, DataAccessException {
					return rse.extractData(rs);
				}

			}, args);
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return t;
	}

	@Override
	public void query(String sql, Object[] args, int[] argTypes, final RowCallbackHandler rch) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		try {
			jdbcTemplate.query(sql, args, argTypes, new org.springframework.jdbc.core.RowCallbackHandler() {

				@Override
				public void processRow(ResultSet rs) throws SQLException {
					rch.processRow(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
	}

	@Override
	public void query(String sql, Object[] args, final RowCallbackHandler rch) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		try {
			jdbcTemplate.query(sql, args, new org.springframework.jdbc.core.RowCallbackHandler() {

				@Override
				public void processRow(ResultSet rs) throws SQLException {
					rch.processRow(rs);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
	}

	@Override
	public void query(String sql, final RowCallbackHandler rch, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		try {
			jdbcTemplate.query(sql, new org.springframework.jdbc.core.RowCallbackHandler() {

				@Override
				public void processRow(ResultSet rs) throws SQLException {
					rch.processRow(rs);
				}

			}, args);
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
	}

	@Override
	public <T> List<T> query(String sql, Object[] args, int[] argTypes, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.query(sql, args, argTypes, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> List<T> query(String sql, Object[] args, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.query(sql, args, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> List<T> query(String sql, final RowMapper<T> rowMapper, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.query(sql, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			}, args);
		} catch (DataAccessException e) {
			throw new SessionException("query: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> T queryForObject(String sql, Object[] args, int[] argTypes, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, args, argTypes, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, Object[] args, final RowMapper<T> rowMapper) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, args, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			});
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, final RowMapper<T> rowMapper, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, new org.springframework.jdbc.core.RowMapper<T>() {

				@Override
				public T mapRow(ResultSet rs, int rowNum) throws SQLException {
					return rowMapper.mapRow(rs, rowNum);
				}

			}, args);
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, Object[] args, int[] argTypes, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, args, argTypes, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, Object[] args, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, args, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public <T> T queryForObject(String sql, Class<T> requiredType, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		T t = null;
		try {
			t = jdbcTemplate.queryForObject(sql, requiredType, args);
		} catch (DataAccessException e) {
			throw new SessionException("queryForObject: " + sql, e);
		}
		return t;
	}

	@Override
	public Map<String, Object> queryForMap(String sql, Object[] args, int[] argTypes) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		Map<String, Object> map = null;
		try {
			map = jdbcTemplate.queryForMap(sql, args, argTypes);
		} catch (DataAccessException e) {
			throw new SessionException("queryForMap: " + sql, e);
		}
		return map;
	}

	@Override
	public Map<String, Object> queryForMap(String sql, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		Map<String, Object> map = null;
		try {
			map = jdbcTemplate.queryForMap(sql, args);
		} catch (DataAccessException e) {
			throw new SessionException("queryForMap: " + sql, e);
		}
		return map;
	}

	@Override
	public <T> List<T> queryForList(String sql, Object[] args, int[] argTypes, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, args, argTypes, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> List<T> queryForList(String sql, Object[] args, Class<T> requiredType) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, args, requiredType);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public <T> List<T> queryForList(String sql, Class<T> requiredType, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<T> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, requiredType, args);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public List<Map<String, Object>> queryForList(String sql, Object[] args, int[] argTypes) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<Map<String, Object>> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, args, argTypes);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public List<Map<String, Object>> queryForList(String sql, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		List<Map<String, Object>> list = null;
		try {
			list = jdbcTemplate.queryForList(sql, args);
		} catch (DataAccessException e) {
			throw new SessionException("queryForList: " + sql, e);
		}
		return list;
	}

	@Override
	public int update(String sql, Object[] args, int[] argTypes) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		int i = 0;
		try {
			i = jdbcTemplate.update(sql, args, argTypes);
		} catch (DataAccessException e) {
			throw new SessionException("update: " + sql, e);
		}
		return i;
	}

	@Override
	public int update(String sql, Object... args) throws SessionException {
		jdbcTemplate.setDataSource(getDataSource());
		int i = 0;
		try {
			i = jdbcTemplate.update(sql, args);
		} catch (DataAccessException e) {
			throw new SessionException("update: " + sql, e);
		}
		return i;
	}

	@Override
	public int[] batchUpdate(String sql, List<Object[]> batchArgs) {
		jdbcTemplate.setDataSource(getDataSource());
		int[] is = new int[0];
		try {
			is = jdbcTemplate.batchUpdate(sql, batchArgs);
		} catch (DataAccessException e) {
			throw new SessionException("batchUpdate: " + sql, e);
		}
		return is;
	}

	@Override
	public int[] batchUpdate(String sql, List<Object[]> batchArgs, int[] argTypes) {
		jdbcTemplate.setDataSource(getDataSource());
		int[] is = new int[0];
		try {
			is = jdbcTemplate.batchUpdate(sql, batchArgs, argTypes);
		} catch (DataAccessException e) {
			throw new SessionException("batchUpdate: " + sql, e);
		}
		return is;
	}
}
